defmodule Servy.Handler do

  @moduledoc "Handles HTTP requests."

  alias Servy.Conv
  alias Servy.BearController
  alias Servy.VideoCam
  alias Servy.Fetcher

  @pages_path Path.expand("../../lib/pages", __DIR__)

  import Servy.Plugins, only: [redirect: 1, log: 1, track: 1]
  import Servy.Parser, only: [parse: 1]

  @doc "Transforms the request into a response"
  def handle(request) do
    # conv = parse(request)
    # conv = route(conv)
    # format_response(conv)
    request
    |> parse
    |> redirect
    |> log
    |> route
    |> track
    |> format_response
  end

  def route(%Conv{ method: "POST", path: "/pledges"} = conv) do
    Servy.PledgeController.create(conv, conv.params)
  end

  def route(%Conv{ method: "GET", path: "/pledges"} = conv) do
    Servy.PledgeController.index(conv)
  end

  def route(%Conv{ method: "GET", path: "/sensors" } = conv) do
    sensor_data = Servy.SensorServer.get_sensor_data()

    %{ conv | status: 200, resp_body: inspect sensor_data }

  end

  def route(%Conv{ method: "GET", path: "/snapshots" } = conv) do
    # Serial
    # snapshot1 = VideoCam.get_snapshot("cam-1")
    # snapshot2 = VideoCam.get_snapshot("cam-2")
    # snapshot3 = VideoCam.get_snapshot("cam-3")
    # snapshots = [snapshot1, snapshot2, snapshot3]
    # Parallel
    IO.puts "Start Time: " <> (Time.utc_now |> Time.to_string)

    snapshots = ["cam-1", "cam-2", "cam-3"]
    |> Enum.map(&Fetcher.async(fn -> VideoCam.get_snapshot(&1) end))
    |> Enum.map(&Fetcher.get_result/1)

    IO.puts "End Time: " <> (Time.utc_now |> Time.to_string)
    %{ conv | status: 200, resp_body: inspect snapshots}

  end

  def route(%Conv{ method: "GET", path: "/hibernate/" <> time} = conv) do
    time |> String.to_integer |> :timer.sleep

    %{ conv | status: 200, resp_body: "Awake!" }
  end

  def route(%Conv{ method: "GET", path: "/kaboom"} = conv) do
    raise "Kaboom!"
    conv
  end

  # def route(conv) do
  #   #TODO: Create a new map that also has the response body:
  #   route(conv, conv.method, conv.path)
  # end

  @doc "route for response"
  def route(%Conv{ method: "GET", path: "/wildthings"} = conv) do
    %{ conv | status: 200, resp_body: "Bears, Lions, Tigers" }
  end

  def route(%Conv{ method: "GET", path: "/bears"} = conv) do
    BearController.index(conv)
  end

  def route(%Conv{ method: "GET", path: "/api/bears" } = conv) do
    Servy.Api.BearController.index(conv)
  end

  def route(%Conv{method: "GET", path: "/bears/" <> id} = conv) do
    params = Map.put(conv.params, "id", id)
    BearController.show(conv, params)
  end

  def route(%Conv{ method: "POST", path: "/bears" } = conv) do
    BearController.create(conv, conv.params)
  end

  @doc "response for about.html request"
  def route(%Conv{ method: "GET", path: "/about"} = conv) do
    @pages_path
      |> Path.join("about.html")
      |> File.read
      |> handle_file(conv)
  end

  def route(%Conv{ path: path } = conv) do
    %{ conv | status: 404, resp_body: "No #{path} here!" }
  end

  @doc "handle func for file request"
  def handle_file({:ok, content}, %Conv{} = conv) do
    %{ conv | status: 200, resp_body: content }
  end

  def handle_file({:error, :enoent}, %Conv{} = conv) do
    %{ conv | status: 404, resp_body: "File not found!" }
  end

  def handle_file({:error, reason}, %Conv{} = conv) do
    %{ conv | status: 500, resp_body: "File error: #{reason}" }
  end

  def format_response(%Conv{} = conv) do
    """
    HTTP/1.1 #{Conv.full_status(conv)}
    Content-Type: #{conv.resp_content_type}
    Content-Length: #{String.length(conv.resp_body)}

    #{conv.resp_body}
    """
  end
end
